package com.ren.scale.config;

import com.alibaba.fastjson.JSON;
import com.ren.scale.constant.MQConstant;
import com.ren.scale.entity.ReclOrderErrorLog;
import com.ren.scale.entity.ReclScaleOrder;
import com.ren.scale.service.ReclOrderErrorLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;

/**
 * @ClassName: RabbitMQConfig
 * @Description: TODO
 * @Author: RZY
 * @DATE: 2022/7/17 9:35
 * @Version: v1.0
 */

@Configuration
@Slf4j
public class RabbitMQConfig {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    ReclOrderErrorLogService orderErrorLogService;

    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    @PostConstruct
    public void setReturnCallback() {
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            //记录日志
            log.info("消息发送失败, 响应码: {}, 失败原因: {}, 交换机: {}, 路由Key: {}, 消息: {}",
                    replyCode, replyText, exchange, routingKey, message);
            //重发消息
            rabbitTemplate.convertAndSend(exchange, routingKey, message);
        });
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                log.info("correlationData: {}, 消息是否收到: {}, 失败原因: {}", correlationData, b, s);
            }
        });
    }

    @Bean
    public MessageRecoverer messageRecoverer() {
        //消费者消息重试失败达到最大次数，将消息以及错误信息记录DB日志表
        return (message, throwable) -> {
            //序列化订单信息
            String orderNo = new String(message.getBody(), StandardCharsets.UTF_8);
            //订单错误日志对象封装
            ReclOrderErrorLog orderErrorLog = new ReclOrderErrorLog();
            orderErrorLog.setOrderNo(Long.valueOf(orderNo))
                    .setErrorMessage(throwable.getMessage());
            //保存错误日志记录
            boolean res = orderErrorLogService.save(orderErrorLog);
            if(res) log.info("消费者失败日志记录成功: {}", orderErrorLog);
        };
    }

    /**
     *
     * @return 订单交换机
     */
    @Bean
    public DirectExchange orderDirectExchange() {
        return new DirectExchange(MQConstant.ORDER_EXCHANGE, true, false);
    }

    /**
     *
     * @return 订单延时队列
     */
    @Bean
    public Queue orderDelayQueue() {
        //构建延时队列
        return QueueBuilder
                .durable(MQConstant.ORDER_DELAY_QUEUE) //队列持久化
                .ttl(10000)  //消息过期时间
                .deadLetterExchange(MQConstant.DEAD_EXCHANGE) //配置死信交换机
                .deadLetterRoutingKey(MQConstant.ORDER_DEAD_ROUTING_KEY) //订单过期路由routingKey
                .build();
    }

    /**
     * 初始化死信交换机
     * @return DirectExchange
     */
    @Bean
    public DirectExchange deadDirectExchange() {
        return new DirectExchange(MQConstant.DEAD_EXCHANGE, true, false);
    }

    @Bean
    public Binding orderDelayBinding(DirectExchange orderDirectExchange, Queue orderDelayQueue) {
        return BindingBuilder
                .bind(orderDelayQueue)  //绑定延迟订单队列
                .to(orderDirectExchange)  //绑定到订单交换机
                .with(MQConstant.ORDER_DELAY_ROUTING_KEY); //绑定路由key
    }

    /**
     *
     * @return 订单延时队列
     */
    @Bean
    public Queue orderDeadQueue() {
        return QueueBuilder.durable(MQConstant.ORDER_DEAD_QUEUE).build();
    }

    @Bean
    public Binding orderDeadBinding(DirectExchange deadDirectExchange, Queue orderDeadQueue) {
        return BindingBuilder
                .bind(orderDeadQueue)  //绑定延迟订单队列
                .to(deadDirectExchange)  //绑定到订单交换机
                .with(MQConstant.ORDER_DEAD_ROUTING_KEY); //绑定路由key
    }
}
